<?php

namespace albert\utils;

use Exception;

/**
 * 文件工具类
 * Class File
 * @package albert\utils
 */
class File
{
    /**
     * 用于显示图片的类型
     * @var string
     */
    public static $img_suffix = 'jpg png jpeg gif bmp ico';

    /**
     * 不可编辑文件类型
     * @var string
     */
    public static $unable_suffix = 'pdf xls xlsx crt pem cer ppt pptx doc docx zip gz tar rar fla jar apk mp3 mp4 rmvb';

    /**
     * 获取目录列表
     * Author: Albert3306
     * Date: 2021/7/6
     * @param string|array $dir_path
     * @return array
     */
    public static function getDir($dir_path = ''): array
    {
        // 如果没有指定访问的目录,则访问当前用户的所有目录
        if (is_array($dir_path)) {
            $result = [];
            foreach((array)$dir_path as $dir_item){
                if(is_file(self::dealPath($dir_item, 'u2g'))){
                    $result[] = self::format_item($dir_item,$dir_item,false,'file');
                }else{
                    $result[] = self::format_item($dir_item,$dir_item,true,'folder');
                }
            }
            return $result;
        } else {
            return self::_getFormatDir($dir_path);
        }
    }

    /**
     * 处理 Windows 下的路径
     * Author: Albert3306
     * Date: 2021/7/6
     * @param $path
     * @param string $type
     * @return array|false|mixed|string|string[]|null
     */
    public static function dealPath($path, string $type = '')
    {
        static $is_win;
        if($is_win === null){
            $is_win = strtoupper(substr(PHP_OS,0,3)) === 'WIN';
        }
        if(!$is_win){
            return $path;
        }

        $path = str_replace("\\", '/', $path);

        if ($type == 'u2g') {
            $path = self::iconv_to($path, 'UTF-8', 'GBK');
        } else if ($type == 'g2u') {
            // 判断编码是否为utf-8
            if (!mb_detect_encoding($path, 'UTF-8', true)) {
                $path = self::iconv_to($path, 'GBK', 'UTF-8');
            }
        }
        return $path;
    }

    /**
     * 转换编码
     * Author: Albert3306
     * Date: 2021/7/6
     * @param $str
     * @param $from
     * @param $to
     * @return array|false|mixed|string|string[]|null
     */
    public static function iconv_to($str, $from, $to){
        if (!function_exists('iconv')){
            return $str;
        }

        if(function_exists('mb_convert_encoding')){
            $result = mb_convert_encoding($str, $to, $from);
        }else{
            $result = iconv($from, $to, $str);
        }
        if(strlen($result) == 0){
            return $str;
        }
        return $result;
    }

    /**
     * 格式化节点信息
     * Author: Albert3306
     * Date: 2021/7/6
     * @param $text
     * @param $id
     * @param $children
     * @param $type
     * @param false $disabled
     * @param false $opened
     * @return array
     */
    public static function format_item($text, $id, $children, $type, bool $disabled = false, bool $opened = false): array
    {
        return [
            'id' => $id,
            'type' => $type,
            'text' => $text,
            'children' => $children,
            'icon'     => self::_get_file_img($text,$type), //$icon,
            'state'    => [
                'disabled' => $disabled,
                'opened' => $opened
            ]
        ];
    }

    /**
     * 设置显示文件
     * Author: Albert3306
     * Date: 2021/7/6
     * @param $path
     * @param $type
     * @return mixed|string
     */
    private static function _get_file_img($path,$type)
    {
        if($type == 'folder') return $type;
        $suffix = pathinfo($path, PATHINFO_EXTENSION);
        if (empty($suffix)) {
            return $type;
        }

        $img = $type;
        $able_icons = 'php js css ico jpg png jpeg gif bmp text txt md log htaccess htm html xml xsl rb pdf as c iso cf cpp cs sql xls xlsx h crt pem cer ppt pptx doc docx zip gz tar rar fla';

        if (strpos($able_icons, $suffix) !== false ) {
            $img = 'file-'.$suffix;
        }

        return $img; // 否则返回一个自己的图片
    }

    /**
     * 获取一个经过格式化的目录
     * Author: Albert3306
     * Date: 2021/7/6
     * @param $path
     * @return array
     */
    private static function _getFormatDir($path): array
    {
        $childs = File::getFileListByDir($path);

        $jsDir = [];
        foreach($childs as $child){
            if( $child['type'] == 'dir' ){
                $jsDir[] = self::format_item($child['name'],$child['path'].'/'.$child['name'],true,'folder');
            }else{
                $jsDir[] = self::format_item($child['name'],$child['path'].'/'.$child['name'],false,'file');
            }
        }
        return $jsDir;
    }

    /**
     * 根据文件夹获取文件中内容
     * Author: Albert3306
     * Date: 2021/7/6
     * @param $path
     * @return mixed|null
     */
    public static function getFileListByDir($path)
    {
        $_path = self::dealPath($path, 'u2g');
        $dirs = scandir($_path);

        $result = [];
        foreach($dirs as $dir_name){
            if ($dir_name !== '.' && $dir_name !== '..') {
                $dir_item = [];
                $dir_item['name'] = self::dealPath($dir_name, 'g2u');
                $dir_item['path'] = $path;
                if (is_dir($_path . '/' . $dir_name)) {
                    $dir_item['type'] = 'dir';
                } else {
                    $dir_item['type'] = 'file';
                }
                $result[] = $dir_item;
            }
        }

        return self::array_order_by($result, 'type', SORT_ASC , 'name', SORT_ASC);
    }

    /**
     * 数组排序
     * Author: Albert3306
     * Date: 2021/7/6
     * @return mixed|null
     */
    public static function array_order_by()
    {
        $args = func_get_args();
        $data = array_shift($args);
        foreach ($args as $n => $field) {
            if (is_string($field)) {
                $tmp = array();
                foreach ($data as $key => $row)
                    $tmp[$key] = $row[$field];
                $args[$n] = $tmp;
            }
        }
        $args[] = &$data;
        call_user_func_array('array_multisort', $args);
        return array_pop($args);
    }

    /**
     * 获取文件信息
     * Author: Albert3306
     * Date: 2021/7/6
     * @param $file_path
     * @return array|string|string[]
     */
    public static function getFileInfo($file_path)
    {
        $file_path = self::dealPath($file_path,'u2g');
        $arr = [
            'is_writeable' => is_writeable($file_path),
        ];
        return array_merge(pathinfo($file_path),$arr);
    }

    /**
     * 获取文件内容
     * Author: Albert3306
     * Date: 2021/7/6
     * @param $file_path
     * @return false|string|null
     */
    public static function getFileContent($file_path)
    {
        $file_path = self::dealPath($file_path,'u2g');
        if(is_file($file_path)){
            return file_get_contents($file_path);
        }else{
            return null;
        }
    }

    /**
     * 将图片转为 Base64
     * Author: Albert3306
     * Date: 2021/7/6
     * @param $image_file
     * @return string
     */
    public static function base64_encode_image($image_file): string
    {
        $image_info = getimagesize($image_file);
        $image_data = fread(fopen($image_file, 'r'), filesize($image_file));
        return 'data:' . $image_info['mime'] . ';base64,' . chunk_split(base64_encode($image_data));
    }

    /**
     * 获取文件
     * Author: Albert3306
     * Date: 2021/7/6
     * @param $file_path
     * @return string
     * @throws Exception
     */
    public static function getFileKey($file_path): string
    {
        $_file_path = self::dealPath($file_path, 'u2g');
        if(!is_file($_file_path)) throw new Exception('文件不存在！', 100);

        // 清除 filemtime 函数缓存
        clearstatcache();
        return md5($file_path . filemtime($_file_path));
    }

    /**
     * 验证文件
     * Author: Albert3306
     * Date: 2021/7/6
     * @param $file_path
     * @param $file_key
     * @return bool
     * @throws Exception
     */
    public static function checkFileKey($file_path, $file_key): bool
    {
        if ($file_key == self::getFileKey($file_path)) return true;
        return false;
    }

    /**
     * 保存文件内容
     * Author: Albert3306
     * Date: 2021/7/6
     * @param $file_path
     * @param string $file_content
     * @param string $open_mode
     * @return bool
     * @throws Exception
     */
    public static function setFileContent($file_path, string $file_content = '' , string $open_mode = 'w'): bool
    {
        $_file_path = self::dealPath($file_path,'u2g');
        if(is_file($_file_path)){
            // 首先我们要确定文件存在并且可写。
            if (is_writable($_file_path)) {
                if (!$handle = fopen($_file_path, $open_mode)) throw new Exception('不能打开文件 ' . $file_path, 100);

                // 将 $some_content 写入到我们打开的文件中。
                if (fwrite($handle, $file_content) === FALSE) throw new Exception('不能写入到文件 ' . $file_path, 101);
                fclose($handle);
                return true;
            } else {
                throw new Exception('没有写文件 ' . $file_path . '权限', 102);
            }
        } else {
            throw new Exception('文件 ' . $file_path . '不存在，请先创建文件,然后写入内容', 102);
        }
    }

    /**
     * 创建目录
     * Author: Albert3306
     * Date: 2021/7/6
     * @param $dir_path
     * @return array|false|mixed|string|string[]|null
     */
    public static function createDir($dir_path)
    {
        $dir_path = self::dealPath($dir_path, 'u2g');
        if (is_dir($dir_path)) {
            return false;
        } else {
            if(!@mkdir($dir_path,0777,true)){
                return false;
            }
            return self::dealPath($dir_path, 'g2u');
        }
    }

    /**
     * 创建文件
     * Author: Albert3306
     * Date: 2021/7/6
     * @param $file_path
     * @return false|mixed
     */
    public static function createFile($file_path)
    {
        $_file_path = self::dealPath($file_path, 'u2g');
        if (is_file($_file_path)) {
            return false;
        } else {
            $dir_path = dirname($file_path);
            self::createDir($dir_path);

            $fp = fopen($_file_path, 'w+');
            fclose($fp);
            return $file_path;
        }
    }

    /**
     * 文件或目录重命名
     * Author: Albert3306
     * Date: 2021/7/6
     * @param $old_path
     * @param $new_name
     * @return array|false|mixed|string|string[]|null
     * @throws Exception
     */
    public static function renameFileOrDir($old_path, $new_name)
    {
        // 判断名称是否合法
        if (!preg_match("#^[\.\w\x{4e00}-\x{9fa5}]+$#u", $new_name)) {
            throw new Exception('文件名称不符合规则[' . $new_name . ']', 101);
        }
        // 将名称改为全路径
        $old_path = self::dealPath($old_path, 'u2g');
        $base_path = pathinfo($old_path, PATHINFO_DIRNAME);
        $new_name = $base_path . '/' . self::dealPath($new_name, 'u2g');

        if (rename($old_path, $new_name)) {
            return self::dealPath($new_name, 'g2u');
        } else {
            return false;
        }
    }

    /**
     * 移动文件
     * Author: Albert3306
     * Date: 2021/7/6
     * @param $from_path
     * @param $to_path
     * @return false|mixed
     */
    public static function moveFileOrDir($from_path, $to_path)
    {
        $from_path = self::dealPath($from_path, 'u2g');
        $_to_path = self::dealPath($to_path, 'u2g');
        if (rename($from_path, $_to_path)) {
            return $to_path;
        } else {
            return false;
        }
    }

    /**
     * 复制文件夹
     * Author: Albert3306
     * Date: 2021/7/6
     * @param $sourceDir
     * @param $destDir
     * @return bool
     */
    public static function copyDir($sourceDir, $destDir): bool
    {
        // 如果是第一次进入,需要处理路径
        static $first_flag;
        if ($first_flag === null) {
            $sourceDir = self::dealPath($sourceDir, 'u2g');
            $destDir = self::dealPath($destDir, 'u2g');
            $first_flag = true;
        }

        if (!is_dir($sourceDir)) {
            return false;
        }

        if (!is_dir($destDir)) {
            // 如果没有目标地址，则尝试创建，失败则返回 false;
            if (!mkdir($destDir)) {
                return false;
            }
        }

        $dir = opendir($sourceDir);
        if (!$dir) {
            return false;
        }

        while (false !== ($file=readdir($dir))) {
            if ($file != '.' && $file != '..') {
                if (is_dir($sourceDir.'/'.$file)) {
                    if (!self::copyDir($sourceDir . '/' . $file, $destDir . '/' . $file)) {
                        return false;
                    }
                } else {
                    // 如果不是目录而是一个文件
                    if (!copy($sourceDir . '/' . $file, $destDir . '/' . $file)) {
                        return false;
                    }
                }
            }
        }
        closedir($dir);

        if ($first_flag !== null) {
            unset($first_flag);
        }
        return true;
    }

    /**
     * 复制文件
     * Author: Albert3306
     * Date: 2021/7/6
     * @param $sourceFile
     * @param $destFile
     * @return bool
     */
    public static function copyFile($sourceFile, $destFile): bool
    {
        $sourceFile = self::dealPath($sourceFile, 'u2g');
        $destFile = self::dealPath($destFile, 'u2g');
        if (copy($sourceFile, $destFile)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 删除文件
     * Author: Albert3306
     * Date: 2021/7/6
     * @param $file_path
     * @return bool
     */
    public static function deleteFile($file_path): bool
    {
        $_file_path = self::dealPath($file_path, 'u2g');
        if(is_file($_file_path)) {
            if (unlink($_file_path)) {
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
    }

    /**
     * 删除目录
     * Author: Albert3306
     * Date: 2021/7/6
     * @param $dir_path
     * @return bool
     */
    public static function deleteDir($dir_path): bool
    {
        // 目录存在则执行删除
        if (!is_dir($dir_path)) return true;

        // 如果是第一次进入，需要处理路径
        static $first_flag;
        if ($first_flag === null) {
            $dir_path = self::dealPath($dir_path, 'u2g');
            $first_flag = true;
        }

        $dh = opendir($dir_path);
        while ($file = readdir($dh)) {
            if ($file != '.' && $file != '..') {
                $fullPath = $dir_path . '/' . $file;
                if (!is_dir($fullPath)) {
                    unlink($fullPath);
                } else {
                    self::deleteDir($fullPath);
                }
            }
        }
        closedir($dh);

        if ($first_flag !== null) {
            unset($first_flag);
        }
        // 删除完当前目录中的内容，再删除当前目录
        if (rmdir($dir_path)) {
            return true;
        }
        return false;
    }
}